React + antd-table 表格拖拽

React + antd-table 表格拖拽class类写法(Immutability Helpers介绍)

第一种方案:react-dnt

主组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { DndProvider } from 'react-dnd';	
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';

moveRow = (dragIndex: number, hoverIndex: number) => {
const { dataSource } = this.state;
const dragRow = dataSource[dragIndex];
this.setState(
// 下面有对这个函数的说明
update(this.state, {
dataSource: {
$splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]],
},
}), () => {
console.log(this.state.dataSource)
}
);
};

public render() {
const { dataSource } = this.state;
<DndProvider backend={HTML5Backend}>
<Table
columns={this.renderColumns()}
dataSource={dataSource}
rowKey="index"
bordered={false}
pagination={false}
locale={{ emptyText: "暂未选择" }}
components={{
body: {
row: DragableBodyRow,
},
}}
onRow={(record, index) => ({
index,
moveRow: this.moveRow,
})}

/>
</DndProvider>
}

组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import React from 'react';
import { DragSource, DropTarget } from 'react-dnd';

let dragingIndex = -1;

class BodyRow extends React.Component<any> {
render() {
const { isOver, connectDragSource, connectDropTarget, moveRow, ...restProps } = this.props;
const style = { ...restProps.style, cursor: 'move' };

let className = restProps.className;
if (isOver) {
if (restProps.index > dragingIndex) {
className += ' drop-over-downward';
}
if (restProps.index < dragingIndex) {
className += ' drop-over-upward';
}
}

return connectDragSource(
connectDropTarget(<tr {...restProps} className={className} style={style} />),
);
}
}

const rowSource = {
beginDrag(props: any) {
dragingIndex = props.index;
return {
index: props.index,
};
},
};

const rowTarget = {
drop(props: any, monitor: any) {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;

// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}

// Time to actually perform the action
props.moveRow(dragIndex, hoverIndex);

// Note: we're mutating the monitor item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
monitor.getItem().index = hoverIndex;
},
};

export const DragableBodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
}))(
DragSource('row', rowSource, connect => ({
connectDragSource: connect.dragSource(),
}))(BodyRow),
);

css

1
2
3
4
5
6
7
8
9

:global {
.drop-over-downward td {
border-bottom: 2px dashed #1890ff!important;
}
.drop-over-upward td {
border-top: 2px dashed #1890ff!important;
}
}

第二种方案:react-sortable-hoc

未做组件抽离,凑合看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { arrayMove, SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';

const SortableItem = SortableElement((props: any) =>
<tr {...props} />
);
const SortableContainers = SortableContainer((props: any) =>
<tbody {...props} />
);

const DragHandle = SortableHandle(() => (
<span className="sortIcon"></span>
));

class AAA extends React.Component<Props, State> {
onSortEnd = (params: any) => {
const { oldIndex, newIndex } = params;
const { dataSource } = this.state;

if (oldIndex !== newIndex) {
const newData = arrayMove(([] as ISettleItemUse[]).concat(dataSource), oldIndex, newIndex).filter((el: any) => !!el);
this.setState({ dataSource: newData });
}
};

DragableBodyRow = (params: any) => {
const { ...restProps } = params
const { dataSource } = this.state;
const index = dataSource.findIndex(x => x.index === restProps['data-row-key']);

return <SortableItem index={index} {...restProps} />
};

render(){
const DraggableContainer = (props: any) => (
<>
<SortableContainers
useDragHandle
helperClass="row-dragging"
onSortEnd={this.onSortEnd}
{...props}
/>
</>
);

<Table
columns={this.renderColumns()}
dataSource={dataSource}
rowKey="index"
bordered={false}
pagination={false}
locale={{ emptyText: "暂未选择" }}
components={{
body: {
wrapper: DraggableContainer,
row: this.DragableBodyRow,
},
}}
/>
}
}


<DragHandle />为拖动的图标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 拖动
.row-dragging {
background: #fafafa;
border: 1px solid #ccc;

th {
color: $color-333;
background: #E9EAEA !important;
font-size: 12px;
padding: 8px 10px !important;
font-weight: normal;
}

td {
font-size: 12px;
padding: 10px 10px !important;
color: $color-333;
visibility: hidden;
}

.drag-visible {
visibility: visible;
}
}

该方案在使用后有点卡顿,时间急没找到 卡顿的原因。使用了第一种方案

可能是跟表格中的动态编辑的select、input有关,setState导致tbody重新渲染,造成卡顿,select和input也有许多联动,目前猜是这个原因,后期有时间,找到一下这个原因。

immutability-helper

yarn add immutability-helper

上文中update $splice

1
2
3
4
5
6
7
8
9
10
11
12
13
const initialArray = ['a', 'b', 'c', 'd', 'e'];

/**
* API: {$splice: array of arrays}
* 同数组的 splice 方法
* 数组 arrays 中包含的是所有需要执行的操作集合
* 元素 array 中第一个元素代表下标,第二个元素代表需要删除的个数,第三个元素代表需要插入到 initialArray 中的的元素
*
* PS: 1、可以在 arrays 中执行多个集合;
* 2、两个操作不是同时执行,而是按顺序执行,后面的操作会在前面一个操作的执行结果上执行
*/
const spliceArray = update(initialArray, { $splice: [[1, 2], [2, 0, 'f', 'g']] });
console.log('spliceArray:', spliceArray); // => [ 'a', 'd', 'f', 'g', 'e' ]

在不更改原始源的情况下改变数据副本。

Immutable data encourages pure functions (data-in, data-out) and lends itself to much simpler application development and enabling techniques from functional programming such as lazy evaluation.

API

$push —— 数组;

$unshift —— 数组;

$splice —— 数组;

$set —— 替换/覆盖/合并原数据;

$toggle —— array of strings ,toggles a list of boolean fields from the target object;

$unset —— remove the list of keys in array from the target object;

$merge —— 合并对象;

$apply —— passes in the current value to the function and updates it with the new returned value;

$add —— 新增;

$remove —— 删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$splice
let arr = [1,2,3,4,5,66];
let arr2 = update(arr,{
$splice : [[1,2,[66788,99],{a:123,b:"fff"}]], // or [0,1,"从我开始是插入的内容",88,89,90,"后面可以很多,是数组、对象、字符串都行"]
});
console.log(arr2);

//复杂一些的用法:
let obj={
name:"immutable",
list :[1,2,[90,55,44,3,22,55],3,4,6,7,8]
};
let obj2 = update(obj,{
list:{
[2]:value=>update(value,{
$splice:[[0,2]] // [90,55,44,3,22,55] => [44, 3, 22, 55]
})
}
});

参考

immutability因React官方出镜之使用总结分享!

https://ant.design/components/table-cn/

https://react-dnd.github.io/react-dnd/docs/overview

感谢你的打赏哦!